import inspect
from inspect import signature
from functools import wraps


def type_validate(func):
    """作为装饰器，用于对函数参数类型进行检验。

    除指定参数类型为python原始数据类型外，包括以下几种情况

    1. 没有指定数据类型的参数；
    2. typing包中的类型；
    3. 自定义类作为参数类型。

    此外，除了能对普通函数进行参数检验外，本装饰器还可以能对类的成员函数进行参数检验。


    Args:
        func (_type_): _description_

    Raises:
        TypeError: _description_
        TypeError: _description_

    Returns:
        _type_: _description_
    """

    sig = signature(func)
    params = sig.parameters

    @wraps(func)
    def wrapper(*args, **kargs):

        paramNames = list(params.keys())
        paramValues = list(args)

        # 对类函数进行处理
        if paramNames[0] in ['self', 'cls']:
            paramNames.pop(0)
            paramValues.pop(0)

        # 对传参时没有显示指定参数名的参数进行校验
        for paramName, paramValue in zip(paramNames, paramValues):
            validate_param_value_type(paramName, paramValue)

        # 对传参时显示指定参数名的参数进行校验
        for paramName, paramValue in kargs.items():
            validate_param_value_type(paramName, paramValue)

        return func(*args, **kargs)

    def validate_param_value_type(paramName, paramValue):
        expectType = params[paramName].annotation

        # 将 typing 中的类型（List、Dict、Tuple等）转换成对应的 python 原始数据类型
        if hasattr(expectType, '__origin__'):
            expectType = expectType.__origin__

        # 没有指定参数类型，任何数据类型都可以作为参数
        if expectType == inspect._empty:
            return

        if not isinstance(paramValue, expectType):
            raise TypeError(f'Argument {paramName} must be {expectType}, but recived {type(paramValue)}.')

    return wrapper


class Foot:
    pass


class H:
    @type_validate
    def __init__(self, a: int, b: int, c: Foot = None) -> None:
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    @type_validate
    def data(cls, data: list):
        pass


if __name__ == '__main__':
    t = H(1, 1, Foot())
    H.data('123')
